// 文件路径: dbms/ui/MainStage.java
package com.dbms.ui;

import com.dbms.common.Column;
import com.dbms.common.QueryResult;
import com.dbms.common.Tuple;
import com.dbms.service.IDbmsService;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;

import java.io.IOException;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * DBMS主功能界面舞台（MainStage）
 * 核心职责：
 * 1. 提供SQL可视化编辑环境（带语法高亮和行号）
 * 2. 展示SQL执行结果（表格形式）
 * 3. 提供功能按钮（执行SQL、UDF管理）
 * 4. 显示系统状态（执行结果、用户信息、事务状态）
 * 5. 依赖{@link IDbmsService}接口实现数据库交互，支持本地/远程服务无缝切换
 *
 * 界面组成：
 * - 工具栏：执行按钮、UDF管理按钮
 * - 编辑区：带语法高亮的SQL代码编辑器（CodeArea）
 * - 结果区：表格形式展示查询结果
 * - 状态栏：显示执行状态、错误信息等
 */
public class MainStage extends Stage {
    // 数据库服务接口：解耦本地/远程实现，由登录后注入（替代原DbmsCore直接依赖）
    private final IDbmsService dbmsService;
    // SQL代码编辑器：支持行号显示和语法高亮（替代传统TextArea，提升开发体验）
    private final CodeArea sqlInputArea;
    // 结果展示表格：用于显示SELECT查询的返回数据，列结构动态适配结果Schema
    private final TableView<ObservableList<String>> resultTableView;
    // 状态标签：显示执行状态（就绪、执行中、成功/错误信息）
    private final Label statusLabel;
    // 执行按钮：触发SQL执行逻辑，点击后禁用防止重复提交
    private final Button executeButton;

    // --- 语法高亮配置：定义SQL关键字、符号等的匹配规则 ---
    // SQL关键字列表：覆盖常用DDL、DML、DCL语句关键字
    private static final String[] KEYWORDS = new String[] {
            "SELECT", "FROM", "WHERE", "INSERT", "INTO", "VALUES",
            "UPDATE", "SET", "DELETE", "CREATE", "TABLE", "PRIMARY", "KEY",
            "JOIN", "ON", "GROUP", "BY", "ORDER", "ASC", "DESC",
            "INT", "VARCHAR", "BEGIN", "TRANSACTION", "COMMIT", "ROLLBACK",
            "USER", "IDENTIFIED", "GRANT", "REVOKE", "DROP", "BACKUP", "DATABASE", "TO"
    };

    // 正则表达式模式：分别匹配SQL中的不同元素
    private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; // 关键字
    private static final String PAREN_PATTERN = "[()]"; // 括号（()）
    private static final String BRACE_PATTERN = "[{}]"; // 大括号（{}）
    private static final String BRACKET_PATTERN = "[\\[\\]]"; // 中括号（[]）
    private static final String SEMICOLON_PATTERN = ";"; // 分号（语句结束符）
    private static final String STRING_PATTERN = "'([^'\\\\]|\\\\.)*'"; // 字符串（单引号包裹）
    private static final String COMMENT_PATTERN = "--[^\n]*"; // 单行注释（--开头）

    // 组合正则模式：统一匹配所有SQL元素，用于语法高亮
    private static final Pattern PATTERN = Pattern.compile(
            "(?<KEYWORD>" + KEYWORD_PATTERN + ")"
                    + "|(?<PAREN>" + PAREN_PATTERN + ")"
                    + "|(?<BRACE>" + BRACE_PATTERN + ")"
                    + "|(?<BRACKET>" + BRACKET_PATTERN + ")"
                    + "|(?<SEMICOLON>" + SEMICOLON_PATTERN + ")"
                    + "|(?<STRING>" + STRING_PATTERN + ")"
                    + "|(?<COMMENT>" + COMMENT_PATTERN + ")"
    );

    /**
     * 构造方法：初始化主界面的布局、组件和事件绑定
     * @param dbmsService 数据库服务接口实例（本地/远程服务）
     */
    public MainStage(IDbmsService dbmsService) {
        this.dbmsService = dbmsService;
        // 设置窗口标题：显示当前登录用户（从服务中获取，确保与认证状态一致）
        setTitle("DBMS 控制台 - 用户: " + dbmsService.getCurrentUser());

        // --- 1. 初始化根布局（BorderPane：上下左右中五个区域，适合复杂界面）---
        BorderPane root = new BorderPane();

        // --- 2. 初始化SQL代码编辑区（带行号和语法高亮）---
        sqlInputArea = new CodeArea();
        // 添加行号工厂：在代码左侧显示行号，便于定位
        sqlInputArea.setParagraphGraphicFactory(LineNumberFactory.get(sqlInputArea));
        // 绑定文本变化事件：文本修改后延迟200ms更新语法高亮（避免频繁计算）
        sqlInputArea.multiPlainChanges()
                .successionEnds(Duration.ofMillis(200))
                .subscribe(ignore -> sqlInputArea.setStyleSpans(0, computeHighlighting(sqlInputArea.getText())));

        // --- 3. 初始化结果展示表格 ---
        resultTableView = new TableView<>();
        // 表格无数据时显示的提示文本
        resultTableView.setPlaceholder(new Label("执行查询后，结果将在此处显示"));
        // 设置表格在垂直方向上占满可用空间（随窗口大小拉伸）
        VBox.setVgrow(resultTableView, Priority.ALWAYS);

        // --- 4. 初始化工具栏（功能按钮区）---
        // 4.1 执行按钮：图标为PLAY，点击触发SQL执行
        executeButton = new Button("执行");
        executeButton.setGraphic(new FontAwesomeIconView(FontAwesomeIcon.PLAY));
        executeButton.setOnAction(e -> executeSql()); // 绑定执行事件

        // 4.2 UDF管理按钮：新增功能，打开UDF管理器窗口（图标用EDIT）
        Button manageUdfButton = new Button("UDF 管理器");
        manageUdfButton.setGraphic(new FontAwesomeIconView(FontAwesomeIcon.EDIT));
        manageUdfButton.setOnAction(e -> {
            // 创建UDF管理窗口，并传入当前主舞台和服务实例
            UdfManagerStage udfManager = new UdfManagerStage(this, dbmsService);
            udfManager.show();
        });

        // 4.3 组装工具栏：执行按钮 + 分隔符 + UDF管理按钮
        ToolBar toolBar = new ToolBar(executeButton, new Separator(), manageUdfButton);

        // --- 5. 初始化状态栏（显示执行状态和提示信息）---
        statusLabel = new Label("就绪");
        statusLabel.setPadding(new Insets(8, 10, 8, 10)); // 设置内边距，提升视觉效果
        statusLabel.getStyleClass().add("status-bar"); // 绑定CSS样式类，便于统一美化
        statusLabel.setMaxWidth(Double.MAX_VALUE); // 状态栏水平方向占满窗口

        // --- 6. 组装中间区域（编辑区 + 结果区，用SplitPane实现可调整大小）---
        SplitPane splitPane = new SplitPane();
        splitPane.setOrientation(javafx.geometry.Orientation.VERTICAL); // 垂直分割（上编辑区，下结果区）
        // 上半部分：SQL编辑区（用VBox包裹，确保拉伸）
        VBox topPane = new VBox(sqlInputArea);
        VBox.setVgrow(sqlInputArea, Priority.ALWAYS);
        // 下半部分：结果表格区（用VBox包裹，确保拉伸）
        VBox centerPane = new VBox(resultTableView);
        VBox.setVgrow(resultTableView, Priority.ALWAYS);
        // 添加到分割面板，并设置初始分割比例（编辑区占40%，结果区占60%）
        splitPane.getItems().addAll(topPane, centerPane);
        splitPane.setDividerPositions(0.4);

        // --- 7. 将各组件组装到根布局 ---
        root.setTop(toolBar); // 顶部：工具栏
        root.setCenter(splitPane); // 中间：分割面板（编辑+结果）
        root.setBottom(statusLabel); // 底部：状态栏

        // --- 8. 初始化场景和样式 ---
        Scene scene = new Scene(root, 1200, 800); // 初始窗口大小：宽1200px，高800px
        // 加载CSS样式文件（美化界面，如按钮、表格、状态栏样式）
        scene.getStylesheets().add(getClass().getResource("/style.css").toExternalForm());
        setScene(scene);

        // --- 9. 绑定窗口关闭事件：释放资源 ---
        setOnCloseRequest(e -> {
            System.out.println("Main window closed. Shutting down service.");
            try {
                dbmsService.close(); // 调用服务的关闭方法（释放数据库连接、文件句柄等）
            } catch (IOException ex) {
                ex.printStackTrace(); // 打印关闭异常（不阻断窗口关闭）
            }
            Platform.exit(); // 退出JavaFX应用
        });
    }

    /**
     * 执行SQL的核心逻辑：校验输入、异步执行、更新UI
     * 设计亮点：用多线程避免UI阻塞（执行SQL时界面仍可响应）
     */
    private void executeSql() {
        // 1. 获取SQL文本并trim（去除前后空格，避免空语句）
        String sql = sqlInputArea.getText();
        if (sql == null || sql.trim().isEmpty()) {
            // 输入为空时更新状态栏提示，标记为错误状态
            statusLabel.setText("请输入 SQL 命令。");
            statusLabel.getStyleClass().remove("success-label");
            statusLabel.getStyleClass().add("error-label");
            return;
        }

        // 2. 禁用执行按钮：防止用户重复点击导致并发执行
        executeButton.setDisable(true);
        statusLabel.setText("正在执行..."); // 更新状态栏为执行中
        statusLabel.getStyleClass().removeAll("success-label", "error-label"); // 清除之前的状态样式

        // 3. 开启新线程执行SQL（关键：避免阻塞JavaFX UI线程）
        new Thread(() -> {
            // 调用服务接口执行SQL（不关心是本地还是远程，统一接口）
            QueryResult result = dbmsService.processSql(sql);

            // 4. 切换回UI线程更新界面（JavaFX要求UI操作必须在主线程）
            Platform.runLater(() -> {
                updateUIWithResult(result); // 根据执行结果更新表格和状态栏
                executeButton.setDisable(false); // 恢复按钮可用状态
            });
        }).start();
    }

    /**
     * 根据SQL执行结果更新UI（表格、状态栏、窗口标题）
     * @param result SQL执行结果（包含成功/失败状态、数据、消息）
     */
    private void updateUIWithResult(QueryResult result) {
        // 1. 清空之前的结果（表格列和数据）
        resultTableView.getColumns().clear();
        resultTableView.getItems().clear();

        // 2. 处理成功结果
        if (result.isSuccess) {
            // 更新状态栏为成功消息，绑定成功样式
            statusLabel.setText(result.message);
            statusLabel.getStyleClass().remove("error-label");
            statusLabel.getStyleClass().add("success-label");

            // 2.1 若结果包含Schema和数据（SELECT查询），填充表格
            if (result.schema != null && result.tuples != null && !result.tuples.isEmpty()) {
                List<Column> columns = result.schema.getColumns();
                // 2.1.1 动态创建表格列（根据结果Schema的列名）
                for (int i = 0; i < columns.size(); i++) {
                    final int colIndex = i; // 内部类引用需为final
                    TableColumn<ObservableList<String>, String> tableColumn = new TableColumn<>(columns.get(i).getColumnName());
                    // 绑定列的单元格值：从行数据的对应索引获取值
                    tableColumn.setCellValueFactory(param ->
                            new SimpleStringProperty(param.getValue().get(colIndex))
                    );
                    resultTableView.getColumns().add(tableColumn);
                }

                // 2.1.2 填充表格数据（将Tuple转换为ObservableList<String>）
                ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
                for (Tuple tuple : result.tuples) {
                    ObservableList<String> row = FXCollections.observableArrayList();
                    for (int i = 0; i < columns.size(); i++) {
                        Object value = tuple.getValue(i);
                        // 空值显示为"NULL"，非空值转换为字符串
                        row.add(value == null ? "NULL" : value.toString());
                    }
                    data.add(row);
                }
                resultTableView.setItems(data);
            }
        }
        // 3. 处理失败结果
        else {
            // 更新状态栏为错误消息，绑定错误样式
            statusLabel.setText("错误: " + result.message);
            statusLabel.getStyleClass().remove("success-label");
            statusLabel.getStyleClass().add("error-label");
        }

        // 4. 更新窗口标题：显示当前用户和事务状态（是否处于事务中）
        setTitle("DBMS 控制台 - 用户: " + dbmsService.getCurrentUser() +
                (dbmsService.isInTransaction() ? " (事务中)" : ""));
    }

    /**
     * 计算SQL文本的语法高亮样式：根据正则匹配结果分配样式类
     * @param text 待高亮的SQL文本
     * @return 样式跨度集合（StyleSpans），用于CodeArea渲染
     */
    private static StyleSpans<Collection<String>> computeHighlighting(String text) {
        Matcher matcher = PATTERN.matcher(text);
        int lastMatchEnd = 0; // 上一次匹配的结束位置
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();

        // 遍历所有匹配结果，为不同SQL元素分配样式类
        while (matcher.find()) {
            // 根据匹配的分组确定样式类（与CSS中的类名对应）
            String styleClass =
                    matcher.group("KEYWORD") != null ? "keyword" :       // 关键字：蓝色
                            matcher.group("PAREN") != null ? "paren" :           // 括号：橙色
                                    matcher.group("BRACE") != null ? "brace" :           // 大括号：橙色
                                            matcher.group("BRACKET") != null ? "bracket" :       // 中括号：橙色
                                                    matcher.group("SEMICOLON") != null ? "semicolon" :   // 分号：红色
                                                            matcher.group("STRING") != null ? "string" :         // 字符串：绿色
                                                                    matcher.group("COMMENT") != null ? "comment" :       // 注释：灰色
                                                                            null; // 理论上不会触发（所有情况已覆盖）

            assert styleClass != null;
            // 1. 添加上一次匹配结束到当前匹配开始的空白样式（无高亮）
            spansBuilder.add(Collections.emptyList(), matcher.start() - lastMatchEnd);
            // 2. 为当前匹配的文本添加对应的样式类
            spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
            // 3. 更新上一次匹配的结束位置
            lastMatchEnd = matcher.end();
        }

        // 处理最后一次匹配到文本末尾的空白样式
        spansBuilder.add(Collections.emptyList(), text.length() - lastMatchEnd);
        // 构建并返回StyleSpans（CodeArea可直接使用）
        return spansBuilder.create();
    }
}